package ${domain}.frame.client;

import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import ${domain}.frame.base.BaseRequest;
import ${domain}.frame.base.BaseResponse;
import ${domain}.frame.base.ErrorType;
import ${domain}.frame.client.CallAfter;
import ${domain}.frame.client.CallBefore;
import ${domain}.frame.client.Callback;
import ${domain}.frame.okhttp3.ProgressRequestBody;
import ${domain}.frame.utils.AESUtil;
import ${domain}.frame.utils.MD5Util;
import ${domain}.frame.utils.MapperUtil;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * ApiClient - 请求客户端
 *
 * @author
 * @version 0.0.1
 * @since 2020-06-18
 */
public class ApiClient {
    public static ApiClient getInstance(String serverUrl, String appKey, String appSecret) {
        return new ApiClient(serverUrl, appKey, appSecret, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT);
    }

    public static ApiClient getInstance(String serverUrl, String appKey, String appSecret, int connectTimeout, int readTimeout) {
        return new ApiClient(serverUrl, appKey, appSecret, connectTimeout, readTimeout);
    }

    //应用码
    private String appKey;
    //应用安全码
    private String appSecret;
    //服务器地址
    private String serverUrl;
    private OkHttpClient httpClient = null;
    //默认参数
    private static final int DEFAULT_CONNECT_TIMEOUT = 5;//秒
    private static final int DEFAULT_READ_TIMEOUT = 10;//秒

    private CallBefore callBefore = null;
    private CallAfter callAfter = null;
    private String token = "";
    private boolean debug = false;

    private ApiClient(String serverUrl, String appKey, String appSecret, int connectTimeout, int readTimeout) {
        this.appKey = appKey;
        this.appSecret = appSecret;
        this.serverUrl = serverUrl;
        this.httpClient = new OkHttpClient.Builder()
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .build();
    }

    public void setCallAfter(CallAfter callAfter) {
        this.callAfter = callAfter;
    }

    public void setCallBefore(CallBefore callBefore) {
        this.callBefore = callBefore;
    }

    public <T extends BaseResponse> T execute(BaseRequest<T> request) {
        return execute(request, null);
    }

    public <T extends BaseResponse> T execute(BaseRequest<T> request, ProgressRequestBody.ProgressListener listener) {
        if (callBefore != null) {
            callBefore.call(request);
        }

        if (debug) {
            System.out.println("debug-->请求内容:" + MapperUtil.toJson(request));
        }

        // 检查请求参数
        T t = MapperUtil.toJava("{}", request.responseClass());

        try {
            //装载请求参数
            String currentTime = String.valueOf(System.currentTimeMillis());
            String base64 = AESUtil.encrypt2Base64(MapperUtil.toJson(request).getBytes(), appSecret);

            RequestBody requestBody = new MultipartBody.Builder()
                    .addFormDataPart("appKey", appKey)
                    .addFormDataPart("encryptData", null, ProgressRequestBody.createProgressRequestBody(base64, listener))
                    .addFormDataPart("sign", sign(request, currentTime))
                    .addFormDataPart("timestamp", currentTime)
                    .addFormDataPart("token", token)
                    .build();

            Request build = new Request.Builder()
                    .url(serverUrl + request.path())
                    .post(requestBody)
                    .build();

            Response response = httpClient.newCall(build).execute();
            if (response.isSuccessful()) {
                String responseString = response.body().string();
                String responseJson = null;
                if (!responseString.startsWith("{")) {
                    if (debug) System.out.println("debug-->加密响应结果:" + responseString);
                    responseJson = AESUtil.decrypt2String(responseString, appSecret);
                    if (debug) System.out.println("debug-->响应结果:" + responseJson);
                } else {
                    responseJson = responseString;
                    if (debug) System.out.println("debug-->响应结果:" + responseJson);
                }
                t = MapperUtil.toJava(responseJson, request.responseClass());
            } else if (404 == response.code()) {
                t.addError(ErrorType.SYSTEM_ERROR, "地址不存在!");
            } else if (403 == response.code()) {
                t.addError(ErrorType.SYSTEM_ERROR, "禁止访问!");
            } else if (500 == response.code()) {
                t.addError(ErrorType.SYSTEM_ERROR, "服务器走了下神!");
            }
        } catch (ConnectException e) {
            t.addError(ErrorType.SYSTEM_ERROR, "网络异常!");
        } catch (SocketTimeoutException e) {
            t.addError(ErrorType.SYSTEM_ERROR, "请求超时!");
        } catch (IOException e) {
            t.addError(ErrorType.SYSTEM_ERROR, "请求异常!");
        } finally {
            if (callAfter != null) {
                callAfter.call(request, t);
            }
        }
        return t;
    }

    public <T extends BaseResponse> void executeCall(BaseRequest<T> request, Callback callback) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                T execute = execute(request, null);
                callback.call(execute);
            }
        });
    }

    public <T extends BaseResponse> void executeCall(BaseRequest<T> request, Callback callback, ProgressRequestBody.ProgressListener listener) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                T execute = execute(request, listener);
                callback.call(execute);
            }
        });
    }

    /**
     * 对请求进行签名
     *
     * @param request
     * @return
     */
    private String sign(BaseRequest request, String currentTime) {
        String json = MapperUtil.toJson(request);
        return MD5Util.encode(appSecret + json + currentTime);
    }

    public void setToken(String token) {
        this.token = token == null ? "" : token;
    }

    public boolean isDebug() {
        return debug;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }
}

